#include <glf/core/macros.h>
#include <glf/core/inlines.h>

#include <glf/core/str.h>
#include <glf/core/mem.h>

#include <glf/app.h>

#ifndef GLF_WINDOWS_DISABLE_SDL

#include <SDL_keyboard.h>
#include <SDL_mouse.h>
#include <SDL_joystick.h>
#include <SDL_events.h>

#endif

#include <limits>

namespace glf {

#ifndef GLF_WINDOWS_DISABLE_SDL

const struct SdlGlfMapping {
	int sdlKey;
	Keyboard::KeyCode glfKey;
} gSdlToGlfMapping[] = {//VK_SCROLL
	{ SDLK_UP,		Keyboard::KEY_UP },			{ SDLK_RIGHT,		Keyboard::KEY_RIGHT },		{ SDLK_DOWN, Keyboard::KEY_DOWN },		{ SDLK_LEFT, Keyboard::KEY_LEFT },

	{ SDLK_ESCAPE,	Keyboard::KEY_ESC },		{ SDLK_BACKSPACE,	Keyboard::KEY_BACK },		{ SDLK_TAB,	Keyboard::KEY_TAB },
	{ SDLK_RETURN,	Keyboard::KEY_RETURN },		{ SDLK_SPACE,		Keyboard::KEY_SPACE },

	{ SDLK_INSERT,	Keyboard::KEY_INSERT },		{ SDLK_HOME,		Keyboard::KEY_HOME },		{ SDLK_PAGEUP,		Keyboard::KEY_PAGEUP },
	{ SDLK_DELETE,	Keyboard::KEY_DEL },		{ SDLK_END,			Keyboard::KEY_END },		{ SDLK_PAGEDOWN,	Keyboard::KEY_PAGEDOWN },

	{ SDLK_LSHIFT,	Keyboard::KEY_LSHIFT },		{ SDLK_RSHIFT,		Keyboard::KEY_RSHIFT },
	{ SDLK_LCTRL,	Keyboard::KEY_LCTRL },		{ SDLK_RCTRL,		Keyboard::KEY_RCTRL },
	{ SDLK_LALT,	Keyboard::KEY_LALT },
	{ SDLK_LSUPER,	Keyboard::KEY_LWIN },		{ SDLK_RSUPER,		Keyboard::KEY_RWIN },

	{ SDLK_PAUSE,	Keyboard::KEY_PAUSE },		{ SDLK_CAPSLOCK,	Keyboard::KEY_CAPSLOCK },

	{ SDLK_NUMLOCK,	Keyboard::KEY_NUMLOCK },	{ SDLK_KP_DIVIDE,	Keyboard::KEY_KPDIV },		{ SDLK_KP_MULTIPLY,	Keyboard::KEY_KPMULT },	{ SDLK_KP_MINUS,	Keyboard::KEY_KPMINUS },
	{ SDLK_KP_PLUS,	Keyboard::KEY_KPPLUS },		{ SDLK_KP_PERIOD,	Keyboard::KEY_KPPERIOD }, //KEY_KPENTER

	{ SDLK_PLUS, Keyboard::KEY_PLUS }, { SDLK_MINUS, Keyboard::KEY_MINUS }, { SDLK_PERIOD, Keyboard::KEY_PERIOD },
	{ SDLK_SLASH, Keyboard::KEY_SLASH }, { SDLK_HASH, Keyboard::KEY_HASH }, { SDLK_EQUALS, Keyboard::KEY_EQUAL}, 
	{ SDLK_QUOTE, Keyboard::KEY_QUOTE }, { SDLK_BACKQUOTE, Keyboard::KEY_BACKQUOTE }, { SDLK_SEMICOLON, Keyboard::KEY_SEMICOLON }, 
	{ SDLK_LEFTBRACKET, Keyboard::KEY_LEFTBRACKET }, { SDLK_RIGHTBRACKET, Keyboard::KEY_RIGHTBRACKET }, 
	{ SDLK_BACKSLASH, Keyboard::KEY_BACKSLASH }, { SDLK_COMMA, Keyboard::KEY_COMMA }, 
};

Keyboard::KeyCode SdlToGlfKey(int vk) {
	if(vk >= SDLK_a && vk <= SDLK_z) {
		return static_cast<Keyboard::KeyCode>(Keyboard::KEY_A + vk - SDLK_a);
	} else if(vk >= SDLK_0 && vk <= SDLK_9) {
		return static_cast<Keyboard::KeyCode>(Keyboard::KEY_0 + vk - SDLK_0);
	} else if(vk >= SDLK_F1 && vk <= SDLK_F12) {
		return static_cast<Keyboard::KeyCode>(Keyboard::KEY_F1 + vk - SDLK_F1);
	} else if(vk >= SDLK_KP0 && vk <= SDLK_KP9) {
		return static_cast<Keyboard::KeyCode>(Keyboard::KEY_KP0 + vk - SDLK_KP0);
	}

	for(size_t i = 0; i < GLF_ASIZEOF(gSdlToGlfMapping); ++i) {
		if(vk == gSdlToGlfMapping[i].sdlKey) {
			return gSdlToGlfMapping[i].glfKey;
		}
	}

	return Keyboard::KEY_NOKEY;
}

struct SdlGlfMouseFlags {
	int sdl;
	int glf;
} gSdlGlfMouseFlags[] = {
	{ SDL_BUTTON(SDL_BUTTON_LEFT),		Mouse::BTN_LEFT },
	{ SDL_BUTTON(SDL_BUTTON_MIDDLE),	Mouse::BTN_MIDDLE },
	{ SDL_BUTTON(SDL_BUTTON_RIGHT),		Mouse::BTN_RIGHT },
	{ SDL_BUTTON(SDL_BUTTON_WHEELUP),	Mouse::BTN_WHEELUP },
	{ SDL_BUTTON(SDL_BUTTON_WHEELDOWN),	Mouse::BTN_WHEELDOWN },
};

#endif

enum Win32GamepadType {
	WIN32GAMEPAD_XBOX360,
	WIN32GAMEPAD_PS2_MP8866DUAL,
	WIN32GAMEPAD_PS3_XX,
	WIN32GAMEPAD_PS3_MOTIONINJOY,
	WIN32GAMEPAD_COUNT,
};

#define EMPTY2	-1,-1,
#define EMPTY4	EMPTY2 EMPTY2
#define EMPTY8	EMPTY4 EMPTY4
#define EMPTY16	EMPTY8 EMPTY8
#define EMPTY32	EMPTY16 EMPTY16

const uint MAX_PADS = 4;
const uint MAX_BTN_PER_GAMEPAD = 32;
const uint AXIS_PER_PAD	= 2;
const uint MAX_AXIS = 4;
const char AXISBUTTON_NEG = char(0x80);
const char AXISBUTTON_POS = char(0x40);

struct GamePadMapping {
	GamepadType type;
	char btnMapping[MAX_BTN_PER_GAMEPAD];
	char dpadMapping[MAX_PADS];
	char dpadBtnMapping[4];
	char apadMapping[MAX_PADS][AXIS_PER_PAD];
	char axisBtnMapping[MAX_BTN_PER_GAMEPAD];
};

#define SET_STR(x) x, sizeof(x)-1

#define EMPTY_MAPPING GAMEPADTYPE_COUNT, { EMPTY32 }, { EMPTY4 }, { EMPTY4 }, { {-1,-1},{-1,-1},{-1,-1},{-1,-1}, }, { EMPTY32 },

//ok, this is attrocious
struct GameControllerMapping {
	const char* name;
	size_t nameLength;
	GamePadMapping gamepadMapping[GAMEPADTYPE_COUNT];
} const gGameControllerMappings[] = {
	{	//WIN32GAMEPAD_XBOX360
		SET_STR("XBOX 360 For Windows (Controller)"),
		{
			{	//emulating xbox360
				GAMEPADTYPE_XBOX360,
				{	//btnMapping
					XboxController::BTN_A,	XboxController::BTN_B,		XboxController::BTN_X,		XboxController::BTN_Y,
					XboxController::BTN_L1,	XboxController::BTN_R1,		XboxController::BTN_BACK,	XboxController::BTN_START,
					XboxController::BTN_L3,	XboxController::BTN_R3,		-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
					0, -1, -1, -1,
					},
					{	//dpadBtnMapping up right down left
						XboxController::BTN_UP, XboxController::BTN_RIGHT, XboxController::BTN_DOWN, XboxController::BTN_LEFT,
					},
					{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 4, 3 },{-1,-1},
						},
						{	//axisBtnMapping
							EMPTY4
							EMPTY4
							-1, 2|AXISBUTTON_NEG, -1, -1,
							2|AXISBUTTON_POS, -1, -1, -1,
							EMPTY16
						},
			},
			{	//emulating for GAMEPADTYPE_PS3
				GAMEPADTYPE_PS3,
				{	//btnMapping
					Ps3Controller::BTN_X,	Ps3Controller::BTN_O,		Ps3Controller::BTN_SQUARE,	Ps3Controller::BTN_TRIANGLE,
						Ps3Controller::BTN_L1,	Ps3Controller::BTN_R1,		Ps3Controller::BTN_SELECT,	Ps3Controller::BTN_START,
						Ps3Controller::BTN_L3,	Ps3Controller::BTN_R3,		-1,							-1,
						EMPTY4
						EMPTY16
				},
				{	//dpadMapping
					0, -1, -1, -1,
					},
					{	//dpadBtnMapping up right down left
						Ps3Controller::BTN_UP, Ps3Controller::BTN_RIGHT, Ps3Controller::BTN_DOWN, Ps3Controller::BTN_LEFT,
					},
					{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 4, 3 },{-1,-1},
						},
						{	//axisBtnMapping
							EMPTY4
								EMPTY4
								-1, 2|AXISBUTTON_NEG, -1, -1,
								-1, 2|AXISBUTTON_POS, -1, -1,
								EMPTY16
						},
							},
							{},	// WII
							{}, // Cube
							{}, // DS
							{}, // iPhone
							{}, // KD
							{}, // SDL
							{	//emulating for GAMEPADTYPE_PSP2
								GAMEPADTYPE_PSP2,
								{	//btnMapping

									PSP2Controller::BTN_CROSS,	PSP2Controller::BTN_CIRCLE,	PSP2Controller::BTN_SQUARE,	PSP2Controller::BTN_TRIANGLE,
										PSP2Controller::BTN_L,		PSP2Controller::BTN_R,		PSP2Controller::BTN_SELECT,	PSP2Controller::BTN_START,
										-1,							-1,							-1,							-1,
										EMPTY4
										EMPTY16
								},
								{	//dpadMapping
									-1, -1, -1, -1,
									},
									{	//dpadBtnMapping up right down left
										PSP2Controller::BTN_UP, PSP2Controller::BTN_RIGHT, PSP2Controller::BTN_DOWN, PSP2Controller::BTN_LEFT,
									},
									{	//apadMapping
										{0, 1,}, {4, 3,}, {-1, -1,}, {-1, -1,},
										},
										{	//axisBtnMapping
											EMPTY4
												EMPTY4
												-1, 2|AXISBUTTON_NEG, -1, -1,
												-1, 2|AXISBUTTON_POS, -1, -1,
												EMPTY16
										},
							},
		},
	},
	{	//WIN32GAMEPAD_XBOX360
		SET_STR("Controller (XBOX 360 For Windows)"),
		{
			{	//emulating xbox360
				GAMEPADTYPE_XBOX360,
				{	//btnMapping
					XboxController::BTN_A,	XboxController::BTN_B,		XboxController::BTN_X,		XboxController::BTN_Y,
					XboxController::BTN_L1,	XboxController::BTN_R1,		XboxController::BTN_BACK,	XboxController::BTN_START,
					XboxController::BTN_L3,	XboxController::BTN_R3,		-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
						0, -1, -1, -1,
				},
				{	//dpadBtnMapping up right down left
					XboxController::BTN_UP, XboxController::BTN_RIGHT, XboxController::BTN_DOWN, XboxController::BTN_LEFT,
				},
				{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 4, 3 },{-1,-1},
				},
				{	//axisBtnMapping
					EMPTY4
					EMPTY4
					-1, 2|AXISBUTTON_NEG, -1, -1,
					 2|AXISBUTTON_POS, -1, -1, -1,
					EMPTY16
				},
			},
			{	//emulating for GAMEPADTYPE_PS3
				GAMEPADTYPE_PS3,
				{	//btnMapping
					Ps3Controller::BTN_X,	Ps3Controller::BTN_O,		Ps3Controller::BTN_SQUARE,	Ps3Controller::BTN_TRIANGLE,
					Ps3Controller::BTN_L1,	Ps3Controller::BTN_R1,		Ps3Controller::BTN_SELECT,	Ps3Controller::BTN_START,
					Ps3Controller::BTN_L3,	Ps3Controller::BTN_R3,		-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
						0, -1, -1, -1,
				},
				{	//dpadBtnMapping up right down left
					Ps3Controller::BTN_UP, Ps3Controller::BTN_RIGHT, Ps3Controller::BTN_DOWN, Ps3Controller::BTN_LEFT,
				},
				{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 4, 3 },{-1,-1},
				},
				{	//axisBtnMapping
					EMPTY4
					EMPTY4
					-1, 2|AXISBUTTON_NEG, -1, -1,
					-1, 2|AXISBUTTON_POS, -1, -1,
					EMPTY16
				},
			},
			{},	// WII
			{}, // Cube
			{}, // DS
			{}, // iPhone
			{}, // KD
			{}, // SDL
			{	//emulating for GAMEPADTYPE_PSP2
				GAMEPADTYPE_PSP2,
				{	//btnMapping

					PSP2Controller::BTN_CROSS,	PSP2Controller::BTN_CIRCLE,	PSP2Controller::BTN_SQUARE,	PSP2Controller::BTN_TRIANGLE,
					PSP2Controller::BTN_L,		PSP2Controller::BTN_R,		PSP2Controller::BTN_SELECT,	PSP2Controller::BTN_START,
					-1,							-1,							-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
						-1, -1, -1, -1,
				},
				{	//dpadBtnMapping up right down left
					PSP2Controller::BTN_UP, PSP2Controller::BTN_RIGHT, PSP2Controller::BTN_DOWN, PSP2Controller::BTN_LEFT,
				},
				{	//apadMapping
						{0, 1,}, {4, 3,}, {-1, -1,}, {-1, -1,},
				},
				{	//axisBtnMapping
					EMPTY4
					EMPTY4
					-1, 2|AXISBUTTON_NEG, -1, -1,
					-1, 2|AXISBUTTON_POS, -1, -1,
					EMPTY16
				},
			},
		},
	},
	{
		SET_STR("MP-8866 Dual USB Joypad"),
		{
			{	//emulating xbox360
				GAMEPADTYPE_XBOX360,
				{	//btnMapping
					XboxController::BTN_Y,		XboxController::BTN_B,		XboxController::BTN_A,		XboxController::BTN_X,
					XboxController::BTN_L2,		XboxController::BTN_R2,		XboxController::BTN_L1,		XboxController::BTN_R1,
					XboxController::BTN_START,	XboxController::BTN_BACK,		-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
						0, -1, -1, -1,
				},
				{	//dpadBtnMapping up right down left
					XboxController::BTN_UP, XboxController::BTN_RIGHT, XboxController::BTN_DOWN, XboxController::BTN_LEFT,
				},
				{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 2, 3 },{-1,-1},
				},
				{	//axisBtnMapping
					EMPTY32
				},
			},
			{	//emulating for GAMEPADTYPE_PS3
				GAMEPADTYPE_PS3,
				{	//btnMapping
					Ps3Controller::BTN_X,	Ps3Controller::BTN_O,		Ps3Controller::BTN_SQUARE,	Ps3Controller::BTN_TRIANGLE,
					Ps3Controller::BTN_L1,	Ps3Controller::BTN_R1,		Ps3Controller::BTN_SELECT,	Ps3Controller::BTN_START,
					Ps3Controller::BTN_L3,	Ps3Controller::BTN_R3,		-1,							-1,
					EMPTY4
					EMPTY16
				},
				{	//dpadMapping
						0, -1, -1, -1,
				},
				{	//dpadBtnMapping up right down left
					Ps3Controller::BTN_UP, Ps3Controller::BTN_RIGHT, Ps3Controller::BTN_DOWN, Ps3Controller::BTN_LEFT,
				},
				{	//apadMapping
						{-1, -1,}, { 0, 1, }, { 4, 3 },{-1,-1},
				},
				{	//axisBtnMapping
					EMPTY32
				},
			},
		},
	},
};

const GameControllerMapping gEmptyMapping = { SET_STR("empty"), {EMPTY_MAPPING } };

const GameControllerMapping& GetGameControllerMapping(const char* name) {
	for(size_t i = 0; i < GLF_ASIZEOF(gGameControllerMappings); ++i) {
		const GameControllerMapping& mapping = gGameControllerMappings[i];
		if(Strcmp(name, mapping.name) == 0) {
			return mapping;
		}
	}

	return gEmptyMapping;
}

struct InputManager::Impl : public input::ImplBase {
	static const int INPUT_COUNT = 4;

	static Impl* mImpl;

	GamepadType mEmulatedGamepadType;

	uint mCurrentInput;

#if GLF_WINDOWS_USE_WIIMOTE
	WiiMoteManager *mWiiMoteManager;
#endif

	struct InputInfo {
		Keyboard		mKeyboard;
		Mouse			mMouse;

		//Win32GamepadType	mWin32GamepadType;
#ifndef GLF_WINDOWS_DISABLE_SDL
#	if	GLF_USE_SDL_JOYSTICK
		SDL_Joystick*		mSdlJoystick;
#	endif
#endif
		//const char*			mJoystickName;

		uint				mCurrentTouchUid;
		bool				mTouchStarted;

		XboxController		mXboxController;
		Ps3Controller		mPs3Controller;
		WiiRemote			mWiiController;
		DsController		mDsController;
		IPhoneController	mIphoneController;
		OpenKodeController	mOpenKodeController;
		GameCubeController	mGameCubeController;
		PSP2Controller		mPsp2Controller;

#ifndef GLF_WINDOWS_DISABLE_SDL
		SdlController		mSdlController;
#endif

		Gamepad* mGamepads[GAMEPADTYPE_COUNT];

		bool mIsSimulatingDoubleTouch;
		bool mSimPositionGathered;
		int mSimPosX;
		int mSimPosY;

		InputInfo() {
#ifndef GLF_WINDOWS_DISABLE_SDL
#	if GLF_USE_SDL_JOYSTICK
			mSdlJoystick = 0;
#	endif
#endif
			mIsSimulatingDoubleTouch = false;
			mSimPositionGathered = false;

			memset(mGamepads, 0, sizeof(mGamepads));
			mGamepads[GAMEPADTYPE_XBOX360]	= &mXboxController;
			mGamepads[GAMEPADTYPE_PS3]		= &mPs3Controller;
			mGamepads[GAMEPADTYPE_WII]		= &mWiiController;
			mGamepads[GAMEPADTYPE_DS]		= &mDsController;
			mGamepads[GAMEPADTYPE_IPHONE]	= &mIphoneController;
			mGamepads[GAMEPADTYPE_KD]		= &mOpenKodeController;
#ifndef GLF_WINDOWS_DISABLE_SDL
			mGamepads[GAMEPADTYPE_SDL]		= &mSdlController;
#endif
			mGamepads[GAMEPADTYPE_GAMECUBE]	= &mGameCubeController;
			mGamepads[GAMEPADTYPE_PSP2]		= &mPsp2Controller;

			//mGamepads[GAMEPADTYPE_XBOX]		= &mXboxController;
			//mGamepads[GAMEPADTYPE_PS2]		= &mPs3Controller;

			mCurrentTouchUid = 0;
			mTouchStarted = false;
		}

		~InputInfo() {
#ifndef GLF_WINDOWS_DISABLE_SDL
#if GLF_USE_SDL_JOYSTICK
			CloseJoystick();
#endif
#endif
		}

		Gamepad* GetGamepad(GamepadType type) {
			return mGamepads[type];
		}

		Gamepad& GetGamepad(const char* name) {
			for(uint i = 0; i < GLF_ASIZEOF(mGamepads); ++i) {
				Gamepad* gamepad = mGamepads[i];
				if(gamepad) {
					if(strcmp(name, gamepad->GetRttiName()) == 0) {
						return *gamepad;
					}
				}

				GLF_ASSERT(false);
				return mOpenKodeController;
			}
			// is this right?
			GLF_ASSERT(false);
			return mOpenKodeController;
		}

#ifndef GLF_WINDOWS_DISABLE_SDL
#if GLF_USE_SDL_JOYSTICK
		void OpenJoystick(int idx) {
			if (!mSdlJoystick) {
				mSdlJoystick = SDL_JoystickOpen(idx);
				for(size_t i = 0; i < GLF_ASIZEOF(mGamepads); ++i)
					mGamepads[i]->UpdateConnectionStatus(true);
			}
		}
		void CloseJoystick() {
			if(mSdlJoystick) {
				SDL_JoystickClose(mSdlJoystick);
				mSdlJoystick = 0;
				for(size_t i = 0; i < GLF_ASIZEOF(mGamepads); ++i)
					mGamepads[i]->UpdateConnectionStatus(false);
			}
		}
#endif
#endif

		void PreUpdateFrame()
		{
			if(mMouse.WasPressed(Mouse::BTN_LEFT) && mKeyboard.IsDown(Keyboard::KEY_LCTRL) && mKeyboard.IsDown(Keyboard::KEY_LSHIFT))
			{
				mSimPositionGathered = true;
				mIsSimulatingDoubleTouch = false;
				mSimPosX = mMouse.GetPos().x;
				mSimPosY = mMouse.GetPos().y;
				Console::Println("position gathered: %d,%d\n", mSimPosX, mSimPosY);
			}		
			
			if(		(mKeyboard.WasReleased(Keyboard::KEY_LCTRL) && mKeyboard.IsUp(Keyboard::KEY_LSHIFT))
				||	(mKeyboard.WasReleased(Keyboard::KEY_LSHIFT) && mKeyboard.IsUp(Keyboard::KEY_LCTRL)) )
			{
				if(mSimPositionGathered && !mIsSimulatingDoubleTouch)
				{
					Console::Println("start simulating\n");
					mIsSimulatingDoubleTouch = true;
				}
				else if(mIsSimulatingDoubleTouch)
				{
					Console::Println("stop simulating\n");
					mIsSimulatingDoubleTouch = false;
					mSimPositionGathered = false;
				}
			}

			int width = 16;
			DebugDisplay& dd = DebugDisplay::Get();

			if(mSimPositionGathered)
			{
				//draw red circle at mSimPosX,mSimPosY
				dd.setColor(DebugDisplay::RED);
				dd.fillRect(mSimPosX - width/2, mSimPosY - width/2, width, width);
			}

			if(mIsSimulatingDoubleTouch && mMouse.IsDown(Mouse::BTN_LEFT))
			{
				int mouseX = mMouse.GetPos().x;
				int mouseY = mMouse.GetPos().y;
				
				//draw green circle at mouseX, mouseY
				dd.setColor(DebugDisplay::GREEN);
				dd.fillRect(mouseX-width/2, mouseY-width/2, width, width);
				
				//draw blue circle at 2*mSimPosX - mouseX, 2*mSimPosY - mouseY
				dd.setColor(DebugDisplay::BLUE);
				dd.fillRect(2*mSimPosX - mouseX-width/2, 2*mSimPosY - mouseY-width/2, width, width);
			}
		}

		void UpdateFrame(bool isCurrentInput) {

#ifndef GLF_WINDOWS_DISABLE_SDL
#if GLF_USE_SDL_JOYSTICK
			if(mSdlJoystick == 0) {
				return;
			}

			//this line isnt optimal...
			const GameControllerMapping& mapping = GetGameControllerMapping(SDL_JoystickName(SDL_JoystickIndex(mSdlJoystick)));

			//gamepad only works by polling
			for(size_t i = 0; i < GLF_ASIZEOF(mGamepads); ++i) {
				InputDevice* gamepad = mGamepads[i];

				//if (i > 0)
				//	break;

				if(gamepad) {

					const GamePadMapping& gpm = mapping.gamepadMapping[i];

					//! Update buttons
					if (gamepad->mBtns.Size() > 0) {

						for(int btn = SDL_JoystickNumButtons(mSdlJoystick); --btn >= 0; ) {
							Uint8 val = SDL_JoystickGetButton(mSdlJoystick, btn);
							char b = gpm.btnMapping[btn];

							gamepad->UpdateButton(b, val != 0);
						}

						for(uint btn = 0; btn < GLF_ASIZEOF(gpm.axisBtnMapping); ++btn) {

							if(btn >= gamepad->mBtns.Size()) {
								break;
							}

							int axis = gpm.axisBtnMapping[btn];

							if(axis != -1) {
								bool isNeg = TestFlag(axis, AXISBUTTON_NEG);
								bool isPos = TestFlag(axis, AXISBUTTON_POS);

								axis &= ~(AXISBUTTON_NEG|AXISBUTTON_POS);

								Sint16 btnVal = SDL_JoystickGetAxis(mSdlJoystick, axis);
								ushort value = 0;

								if(isNeg && btnVal > 10) {
									value = btnVal*2;
								} else if(isPos && btnVal < -10) {
									value = -btnVal*2;
								}
								gamepad->mBtns[btn].SetValue(value);
								gamepad->mBtns[btn].UpdateFrame(value > 10);
							}
						}

					}

					//! Update anologic sticks
					if (gamepad->mAnalogicSticks.Size() > 0) {

						for(int hat = 0; hat < SDL_JoystickNumHats(mSdlJoystick); ++hat) {
							Uint8 h = SDL_JoystickGetHat(mSdlJoystick, hat);

							gamepad->UpdateButton(gpm.dpadBtnMapping[0], TestFlag(h, SDL_HAT_UP));
							gamepad->UpdateButton(gpm.dpadBtnMapping[1], TestFlag(h, SDL_HAT_RIGHT));
							gamepad->UpdateButton(gpm.dpadBtnMapping[2], TestFlag(h, SDL_HAT_DOWN));
							gamepad->UpdateButton(gpm.dpadBtnMapping[3], TestFlag(h, SDL_HAT_LEFT));


							char stickIdx = gpm.dpadMapping[hat];
							if(stickIdx >= 0) {
								float x = 0.0f, y = 0.0f;
								AnalogicStick &stick = gamepad->mAnalogicSticks[stickIdx];
								switch(SDL_JoystickGetHat(mSdlJoystick, hat))
								{
								case SDL_HAT_UP:
									y = 1.0f;
									break;
								case SDL_HAT_RIGHT:
									x = 1.0f;
									break;
								case SDL_HAT_DOWN:
									y = -1.0f;
									break;
								case SDL_HAT_LEFT:
									x = -1.0f;
									break;
								case SDL_HAT_RIGHTUP:
									x = 1.0f;
									y = 1.0f;
									break;
								case SDL_HAT_RIGHTDOWN:
									x = 1.0f;
									y = -1.0f;
									break;
								case SDL_HAT_LEFTUP:
									x = -1.0f;
									y = 1.0f;
									break;
								case SDL_HAT_LEFTDOWN:
									x = -1.0f;
									y = -1.0f;
									break;
								}
								gamepad->UpdateStick(stickIdx, x, y);
							}
						}

						for(uint padIdx = 0; padIdx < GLF_ASIZEOF(gpm.apadMapping); ++padIdx) {

							if(padIdx >= gamepad->mAnalogicSticks.Size())
								break;

							int xAxisIdx = gpm.apadMapping[padIdx][0];
							int yAxisIdx = gpm.apadMapping[padIdx][1];

							if(xAxisIdx >= 0 && yAxisIdx >= 0) {
								Sint16 xVal = SDL_JoystickGetAxis(mSdlJoystick, xAxisIdx);
								Sint16 yVal = -SDL_JoystickGetAxis(mSdlJoystick, yAxisIdx) -1;

								gamepad->UpdateStick(padIdx, 
									(float) xVal / std::numeric_limits<Sint16>::max(), 
									(float) yVal / std::numeric_limits<Sint16>::max());
							}
						}
					}
				}
			}
#endif
#endif

		}

		void touchBegin(int emulatedGamepadType, int x, int y)
		{
			Gamepad *gamePad = mGamepads[emulatedGamepadType];
			if (!gamePad || gamePad->mTouchPads.Size() == 0)
				return;
		
			gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_BEGAN, mCurrentTouchUid, (float)x, (float)y);
			if(mIsSimulatingDoubleTouch)
				gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_BEGAN, 1, (float)(2*mSimPosX - x), (float)(2*mSimPosY - y));
			mTouchStarted = true;
		}

		void touchMove(int emulatedGamepadType, int x, int y)
		{
			if (!mTouchStarted)
				return;
			Gamepad *gamePad = mGamepads[emulatedGamepadType];
			if (!gamePad || gamePad->mTouchPads.Size() == 0)
				return;

			gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_MOVED, mCurrentTouchUid, (float)x, (float)y);
			if(mIsSimulatingDoubleTouch)
				gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_MOVED, 1, (float)(2*mSimPosX - x), (float)(2*mSimPosY - y));
		}

		void touchEnd(int emulatedGamepadType, int x, int y, int touchId = -1)
		{
			Gamepad *gamePad = mGamepads[emulatedGamepadType];
			if (!gamePad || gamePad->mTouchPads.Size() == 0)
				return;
			gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_ENDED, mCurrentTouchUid, (float)x, (float)y);
			if(mIsSimulatingDoubleTouch)
				gamePad->RaiseTouchEvent(0, glf::IET_TOUCHPAD_ENDED, 1, (float)(2*mSimPosX - x), (float)(2*mSimPosY - y));
			mTouchStarted = false;
			//mCurrentTouchUid++;
		}

	} mInputs[INPUT_COUNT];

	Impl(InputManager& inputMgr) 
	:	ImplBase(inputMgr)
	{
		mImpl = this;

		mCurrentInput = 0;

		mEmulatedGamepadType = GAMEPADTYPE_IPHONE;

#ifndef GLF_WINDOWS_DISABLE_SDL
#	if GLF_USE_SDL_JOYSTICK
		SDL_InitSubSystem(SDL_INIT_JOYSTICK);
#	endif
#endif

#if GLF_WINDOWS_USE_WIIMOTE
		mWiiMoteManager = new WiiMoteManager(INPUT_COUNT);
#endif
	}

	~Impl() {
#ifndef GLF_WINDOWS_DISABLE_SDL
#	if GLF_USE_SDL_JOYSTICK
		for(size_t i = 0; i < GLF_ASIZEOF(mInputs); ++i) {
			mInputs[i].CloseJoystick();
		}
		SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
#	endif
#endif
#if GLF_WINDOWS_USE_WIIMOTE
		delete mWiiMoteManager;
#endif

	}

	void PreUpdateFrame()
	{
		mInputs[mCurrentInput].PreUpdateFrame();
	}

	void UpdateFrame() {
#ifndef GLF_WINDOWS_DISABLE_SDL
		//Gather events from the input devices.
		SDL_PumpEvents();
		SDL_Event evt;
		
		//Check mouse event queue
		InputInfo &inputInfo = mInputs[mCurrentInput];
		while(SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_MOUSEEVENTMASK) == 1) {
			static const Mouse::Btn sSdlMapping[] = {
				Mouse::BTN_NONE,
				Mouse::BTN_LEFT, Mouse::BTN_MIDDLE, Mouse::BTN_RIGHT,
				Mouse::BTN_WHEELUP, Mouse::BTN_WHEELDOWN,
				Mouse::BTN_NONE, Mouse::BTN_NONE
			};

			if(evt.type == SDL_MOUSEMOTION) {
				//mouse move event
				inputInfo.mMouse.RaiseMoveEvent(evt.motion.x, evt.motion.y);
				//touchpad emulation
				inputInfo.touchMove(mEmulatedGamepadType, evt.button.x, evt.button.y);
			}
			else {
				const Mouse::Btn btn = sSdlMapping[evt.button.button];
				//button event
				inputInfo.mMouse.RaiseButtonEvent(btn, evt.type == SDL_MOUSEBUTTONDOWN);
				//special mouse wheel event
				if (evt.type == SDL_MOUSEBUTTONDOWN &&
					(btn == Mouse::BTN_WHEELUP || btn == Mouse::BTN_WHEELDOWN)) {
					inputInfo.mMouse.RaiseWheelEvent(btn == Mouse::BTN_WHEELUP ? 1.0f : -1.0f);
				}
				//touchpad emulation
				else if (evt.type == SDL_MOUSEBUTTONDOWN && btn == Mouse::BTN_LEFT)
				{
					inputInfo.touchBegin(mEmulatedGamepadType, evt.button.x, evt.button.y);
				}
				else if (evt.type == SDL_MOUSEBUTTONUP && btn == Mouse::BTN_LEFT)
				{
					inputInfo.touchEnd(mEmulatedGamepadType, evt.button.x, evt.button.y);
				}
			}
		}

		//Check keyboard event queue
		while(SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_KEYEVENTMASK) == 1) {
			Keyboard::KeyCode key = SdlToGlfKey(evt.key.keysym.sym);
			if(key != Keyboard::KEY_NOKEY)
				//keyboard button event
				inputInfo.mKeyboard.RaiseButtonEvent(key, evt.type == SDL_KEYDOWN);
		}


#if GLF_USE_SDL_JOYSTICK
		SDL_JoystickUpdate();
		//todo: check joystick connection status
		for(size_t i = 0; i < GLF_ASIZEOF(mInputs); ++i) {
			mInputs[i].OpenJoystick(i);
			mInputs[i].UpdateFrame(i == mCurrentInput);
		}
#endif
#endif
#if GLF_WINDOWS_USE_WIIMOTE
		mWiiMoteManager->UpdateFrame();
#endif
	}

	Keyboard& GetKeyboard(uint idx) {
		return mInputs[idx].mKeyboard;
	}

	Mouse& GetMouse(uint idx) {
		return mInputs[idx].mMouse;
	}

	Gamepad& GetGamepad(uint idx) {
		Gamepad* gamepad = mInputs[idx].GetGamepad(mEmulatedGamepadType);
		GLF_ASSERT(gamepad);
		return *gamepad;
	}

	size_t GetKeyboardCount() const
	{
		return INPUT_COUNT;
	}

	size_t GetMouseCount() const
	{
		return INPUT_COUNT;
	}

	size_t GetGamepadCount() const
	{
		return INPUT_COUNT;
	}


	void EmulateGamepad(GamepadType type) {
		mEmulatedGamepadType = type;
	}

};

InputManager::Impl* InputManager::Impl::mImpl = 0;

} //namespace glf

